import { useZIndex } from 'element-plus'
import { isElement, isObject } from 'lodash'
import { createVNode, nextTick, ref, render } from 'vue'
import type { AppContext, VNode, ComponentPublicInstance } from 'vue'
import type { BDialog, BDialogFn, BDialogProps, BDialogQueue } from './dialog'
import BDialogConstructor from './index.vue'
import { withInstallFunction } from 'element-plus/es/utils/vue/install.mjs'

export const bdialogQueue = ref<BDialogQueue>([])
let seed = 1
let appendTo: HTMLElement | null
window.bDialogLeft = 150
window.bDialogTop = 40
const step = 40

const BDialog: BDialogFn & Partial<BDialog> & { _context: AppContext | null } =
  function (options?: any, context?: AppContext | null) {
    if (!isObject(options)) return { close: () => undefined }
    const { nextZIndex } = useZIndex()
    const id = `b-dialog_${seed++}`
    const userOnClose = (options as any).close
    const props: Partial<BDialogProps> = {
      zIndex: nextZIndex(),
      ...options,
      left: window.bDialogLeft,
      top: window.bDialogTop,
      id,
      onClose: (res) => {
        close(id, userOnClose, res)
      },
    }

    appendTo = document.getElementById('dialog-box')
    const { width, height } = appendTo?.getBoundingClientRect()
    if (window.bDialogLeft + (options.width || 300) - 20 < width) {
      window.bDialogLeft += step
    } else {
      window.bDialogLeft = 150
    }
    if (window.bDialogTop + (options.height || 200) - 20 < height) {
      window.bDialogTop += step
    } else {
      window.bDialogTop = 40
    }

    if (!isElement(appendTo)) {
      new Error(
        '[UIBDialog] - the appendTo option is not an HTMLElement. Falling back to document.body.'
      )
      appendTo = document.body
    }

    const container = document.createElement('div')
    container.className = `container_${id}`
    // 转换为虚拟dom
    const vm = createVNode(BDialogConstructor, props, null)

    vm.appContext = context || BDialog._context

    // 创建干净的元素，防止内存泄漏
    vm.props!.onDestroy = () => {
      render(null, container)
    }
    // 将虚拟dom转为真实dom
    render(vm, container)

    bdialogQueue.value.push({ vm, el: container.firstElementChild as Node });
    (appendTo as HTMLElement).appendChild(container.firstElementChild as Node)

    return {
      vm,
      close: () => {
        ((
          vm.component!.exposed as ComponentPublicInstance<{
            close: Function
          }>
        ).close())
      }
    }
  }

export function close (id: string, userOnClose?: (vm: VNode) => void, res?: any): void {
  const idx = bdialogQueue.value.findIndex(({ vm }) => id === vm.component!.props.id)
  if (idx === -1) return

  const { vm } = bdialogQueue.value[idx]
  if (!vm) return
  userOnClose?.(res)
  bdialogQueue.value.splice(idx, 1)
}

export function recoverZindex () {
  for (let i = bdialogQueue.value.length - 1; i >= 0; i--) {
    const instance = bdialogQueue.value[i].vm.component;
    (instance?.exposed as any).recoverZindex()

  }
}

export function closeAll (): void {
  for (let i = bdialogQueue.value.length - 1; i >= 0; i--) {
    const instance = bdialogQueue.value[i].vm.component;
    (instance?.exposed as any)?.handleDelete()
  }
}

export const $bDialog = withInstallFunction(BDialog, '$bDialog')

BDialog.close = close
BDialog.closeAll = closeAll
BDialog._context = null
BDialog.recoverZindex = recoverZindex

export default BDialog
